<template>
  <div class="package-method-page">
    <header class="page-header">
      <div class="title-wrap">
        <h2>包装方式</h2>
        <span class="sub">维护包装 BOM 组合 / 成本构成</span>
      </div>
      <div class="actions">
        <div class="search-box">
          <svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <circle cx="11" cy="11" r="8" />
            <path d="m21 21-4.35-4.35" />
          </svg>
          <input v-model="keyword" placeholder="搜索 包装方式名称" @input="debouncedSearch(1)" @keyup.enter="search(1)" />
        </div>
        <select v-model="statusFilter" class="status-select" @change="debouncedSearch(1)">
          <option value="">全部状态</option>
          <option value="enabled">启用</option>
          <option value="disabled">废弃</option>
        </select>
        <button class="btn primary elevate create" @click="openCreate" aria-label="新建包装方式">
          <span class="icon-wrap">
            <svg class="btn-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <line x1="12" y1="5" x2="12" y2="19" />
              <line x1="5" y1="12" x2="19" y2="12" />
            </svg>
          </span>
          <span class="txt">新建包装方式</span>
        </button>
      </div>
    </header>

    <section v-if="!loading && packages.length" class="table-wrapper card">
      <table class="entry-table fancy">
        <thead>
          <tr>
            <th style="width:200px">包装方式名称</th>
            <th style="width:100px">状态</th>
            <th style="width:120px">包装BOM</th>
            <th style="width:120px">包装成本</th>
            <th style="width:140px">创建时间</th>
            <th style="width:140px">修改时间</th>
            <th style="width:130px">操作</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="p in packages" :key="p.id">
            <td class="strong">{{ p.name }}</td>
            <td><span class="status-badge" :class="p.status">{{ p.status === 'enabled' ? '启用' : '废弃' }}</span></td>
            <td>
              <button type="button" class="btn view sm" @click="viewBom(p)"><span class="txt">查看</span></button>
            </td>
            <td class="cost-val">{{ formatCost(calcPackageCost(p)) }}</td>
            <td>{{ formatDate(p.createdAt) }}</td>
            <td>{{ formatDate(p.updatedAt) }}</td>
            <td class="ops">
              <button class="btn ghost sm blue" @click="edit(p)">编辑</button>
            </td>
          </tr>
          <tr v-if="!packages.length"><td colspan="7" class="empty-tip" style="padding:2rem 0">暂无数据</td></tr>
        </tbody>
      </table>
      <div class="pagination">
        <button class="btn ghost sm" :disabled="currentPage <= 1 || loading" @click="search(currentPage - 1)">上一页</button>
        <span class="pager-info">第 {{ currentPage }} / {{ totalPages }} 页 (共 {{ total }} 条)</span>
        <button class="btn ghost sm" :disabled="currentPage >= totalPages || loading || totalPages <= 1" @click="search(currentPage + 1)">下一页</button>
      </div>
    </section>
    <p v-else-if="!loading" class="empty-tip">暂无包装方式，请新建。</p>
    <div v-else class="loading-mask">加载中...</div>

    <!-- 编辑/新建对话框 -->
    <PackageMethodDialog 
      ref="dialogRef" 
      @save="handleSave"
    />

  <!-- BOM 查看对话框（统一复用组件） -->
  <BomViewDialog ref="bomViewRef" />
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { searchPackages, addPackage, editPackage } from '../../services/package';
import PackageMethodDialog from './components/PackageMethodDialog.vue';
import BomViewDialog from '../bomSimple/components/BomViewDialog.vue';

interface BomItem {
  id?: string;
  _localId?: string;
  seq: number;
  type: 'production' | 'packaging';
  materialCode: string;
  materialName: string;
  costName: string;
  category: string;
  materialCategory?: string; // 分类 ID（字符串形式）
  classId?: number | string; // 分类 ID（后端 classId）
  spec: string;
  qty: number;
  unit: string;
  lossFactor: number;
  unitCost: number;
  attributes: Record<string, any>;
  attributeDefinitions?: any[];
  attributeValues?: Record<string, any>;
  feeDetail?: {
    mode: 'fixed' | 'count';
    amount: number;
    quantity?: number;
  };
}

interface PackageMethod {
  id: string;
  name: string;
  pic?: string;
  bomItems: BomItem[];  // 内部转换后的 BOM 格式
  status: 'enabled' | 'disabled';
  createdAt: number;  // 时间戳
  updatedAt: number;  // 时间戳
}

const keyword = ref('');
const statusFilter = ref('');
const packages = ref<PackageMethod[]>([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const totalPages = ref(0);
const loading = ref(false);
const error = ref('');

const dialogRef = ref<InstanceType<typeof PackageMethodDialog> | null>(null);
const bomViewRef = ref<InstanceType<typeof BomViewDialog> | null>(null);

// 防抖搜索计时器
let searchDebounceTimer: number | null = null;
const DEBOUNCE_DELAY = 500; // 毫秒

// 搜索/加载数据
async function search(targetPage: number) {
  loading.value = true;
  error.value = '';
  try {
    currentPage.value = targetPage;
    const payload = {
      // 后端约定：请求参数使用 page / pageSize
      page: currentPage.value,
      pageSize: pageSize.value,
      name: keyword.value.trim(),
      status: statusFilter.value || undefined
    };
    
    const res = await searchPackages(payload);
    if (res.code !== 200 && res.code !== 0) {
      throw new Error(res.msg || '查询失败');
    }
    
    const data = res.data || {};
    // 转换后端格式到内部格式
    packages.value = (data.records || []).map((record: any) => convertFromApiResponse(record));
  total.value = data.total || 0;
  const resolvedPageSize = data.pageSize ?? data.size ?? pageSize.value;
  if (resolvedPageSize) pageSize.value = resolvedPageSize;
  const resolvedCurrent = data.page ?? data.current;
  if (resolvedCurrent) currentPage.value = resolvedCurrent;
  const resolvedPages = data.totalPages ?? data.pages;
  totalPages.value = resolvedPages || Math.ceil(pageSize.value ? total.value / pageSize.value : 0);
  } catch (e: any) {
    error.value = e.message || '查询失败';
    packages.value = [];
  } finally {
    loading.value = false;
  }
}

// 防抖搜索函数
function debouncedSearch(targetPage: number = 1) {
  if (searchDebounceTimer !== null) {
    clearTimeout(searchDebounceTimer);
  }
  searchDebounceTimer = window.setTimeout(() => {
    search(targetPage);
    searchDebounceTimer = null;
  }, DEBOUNCE_DELAY);
}

// 将后端 API 响应转换为内部格式
function convertFromApiResponse(record: any): PackageMethod {
  const bomItems = convertBomSToItems(record.bomS || []);
  
  // 将时间字符串转换为时间戳
  const createTime = record.createTime ? new Date(record.createTime.replace(/-/g, '/')).getTime() : Date.now();
  const updateTime = record.updateTime ? new Date(record.updateTime.replace(/-/g, '/')).getTime() : Date.now();
  
  return {
    id: String(record.id),
    name: record.name || '',
    pic: record.pic || '',
    bomItems: bomItems,
    status: record.status || 'enabled',
    createdAt: createTime,
    updatedAt: updateTime
  };
}

// 将后端 bomS 格式转换为内部 BomItem 格式
function convertBomSToItems(bomS: Array<{ bom: any; quantity: number }>): BomItem[] {
  return (bomS || []).map((item, idx) => {
    const bom = item.bom || {};
    
    // 将属性数组转换为对象和定义
    const attributeValues: Record<string, any> = {};
    const attributeDefinitions: any[] = [];
    if (Array.isArray(bom.attributes)) {
      bom.attributes.forEach((attr: any) => {
        attributeValues[attr.propertyName] = attr.propertyValue;
        attributeDefinitions.push({
          code: attr.propertyName,
          name: attr.propertyName,
          control: 'TextBox',
          sort: typeof attr.sort === 'number' ? attr.sort : undefined,
          propertyName: attr.propertyName
        });
      });
    }
    
    // 确定费用模式
    const feeMode = bom.feeModel === 'count' ? 'count' : 'fixed';
    
    return {
      id: bom.id || '',
      _localId: `api-${bom.id || idx}`,
      seq: idx + 1,
      type: bom.bomType === 'package' ? 'packaging' : 'production',
      materialCode: bom.invCode || '',
      materialName: bom.name || '',
      costName: bom.name || '',
      category: bom.className || '',
      materialCategory: String(bom.classId || ''),
      classId: bom.classId || undefined,
      spec: '',
      qty: item.quantity || 1,
      unit: 'pcs',
      lossFactor: 1,
      unitCost: 0,
      attributes: attributeValues,
      attributeDefinitions: attributeDefinitions.length > 0 ? attributeDefinitions : Object.keys(attributeValues).map((key, i) => ({
        code: key,
        name: key,
        control: 'TextBox',
        sort: i + 1
      })),
      attributeValues: attributeValues,
      feeDetail: {
        mode: feeMode,
        amount: bom.feeAmount || 0,
        quantity: feeMode === 'count' ? (item.quantity || 1) : undefined
      }
    };
  });
}

function openCreate() {
  dialogRef.value?.open();
}

function edit(p: PackageMethod) {
  dialogRef.value?.open(p);
}

function viewBom(p: PackageMethod) {
  bomViewRef.value?.open({ title: '包装BOM明细 - ' + p.name, bomItems: p.bomItems, group: false });
}

async function handleSave(payload: any) {
  try {
    loading.value = true;
    if (payload.id) {
      // 编辑
      const res = await editPackage({
        id: payload.id,
        name: payload.name,
        pic: payload.pic || '',
        bomS: convertBomItems(payload.bomItems),
        status: payload.status || 'enabled'
      });
      if (res.code !== 200 && res.code !== 0) {
        throw new Error(res.msg || '编辑失败');
      }
      search(currentPage.value);
    } else {
      // 新建
      const res = await addPackage({
        name: payload.name,
        pic: payload.pic || '',
        bomS: convertBomItems(payload.bomItems),
        status: payload.status || 'enabled'
      });
      if (res.code !== 200 && res.code !== 0) {
        throw new Error(res.msg || '新建失败');
      }
      search(1);
    }
  } catch (e: any) {
    alert(e.message || '保存失败');
  } finally {
    loading.value = false;
  }
}

// 将本地 BOM 格式转换为后端期望的格式
// 后端期望: { bom: { bomType, classId, className, id, invCode, name, productType, attributes, feeModel, feeAmount, createTime, updateTime }, quantity }
function convertBomItems(bomS: BomItem[]) {
  return bomS.map(item => {
    // 构建属性数组：优先使用 attributeDefinitions 中的 sort，否则使用递增
    const attributes: Array<{ propertyName: string; propertyValue: string | number; sort: number }> = [];
    if (item.attributeDefinitions && Array.isArray(item.attributeDefinitions) && item.attributeDefinitions.length > 0) {
      item.attributeDefinitions.forEach((def: any, idx: number) => {
        const value = item.attributeValues ? item.attributeValues[def.code] : '';
        attributes.push({
          propertyName: def.name || def.code,
          propertyValue: value || '',
          sort: typeof def.sort === 'number' ? def.sort : (idx + 1) * 10
        });
      });
    } else if (item.attributeValues && Object.keys(item.attributeValues).length > 0) {
      let sortIndex = 1;
      Object.entries(item.attributeValues).forEach(([key, value]) => {
        attributes.push({
          propertyName: key,
          propertyValue: value || '',
          sort: sortIndex++ * 10
        });
      });
    }
    // 确定费用模式和金额
    let feeModel: 'fixed' | 'count' = 'fixed';
    let feeAmount = 0;
    if (item.feeDetail) {
      feeModel = item.feeDetail.mode === 'count' ? 'count' : 'fixed';
      feeAmount = item.feeDetail.amount || 0;
    } else {
      feeAmount = item.unitCost || 0;
    }

    // 获取当前时间戳格式
    const now = new Date();
    const formatDateTime = () => {
      const pad = (n: number) => String(n).padStart(2, '0');
      return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
    };
    const timeStr = formatDateTime();

    return {
      bom: {
        bomType: item.type === 'packaging' ? 'package' : 'product',
        classId: item.classId !== undefined ? item.classId : (item.materialCategory ? parseInt(item.materialCategory) : 0),
        className: item.category, // 中文分类名称
        id: item.id || '',
        invCode: item.materialCode || '',
        name: item.costName || item.materialName,
        productType: '通用', // 包装方式的产品类型固定为通用
        attributes: attributes,
        feeModel: feeModel,
        feeAmount: feeAmount,
        createTime: timeStr,
        updateTime: timeStr
      },
      quantity: item.feeDetail?.mode === 'count' ? (item.feeDetail.quantity || item.qty || 1) : item.qty || 1
    };
  });
}

function formatDate(ts: number) {
  if (!ts) return '-';
  const d = new Date(ts);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
}

// 包装成本：计算所有 BOM 行的总成本（与 PackageBomViewDialog 中的计算一致）
function calcPackageCost(p: PackageMethod) {
  if (!p.bomItems || !p.bomItems.length) return 0;
  return p.bomItems.reduce((sum: number, item: BomItem) => {
    // 计算单行成本（使用与 PackageBomViewDialog 中相同的逻辑）
    let lineCost = 0;
    if (item.feeDetail) {
      if (item.feeDetail.mode === 'fixed') {
        lineCost = item.feeDetail.amount || 0;
      } else {
        lineCost = (item.feeDetail.amount || 0) * (item.feeDetail.quantity || item.qty || 1);
      }
    } else {
      lineCost = (item.unitCost || 0) * item.qty * (item.lossFactor || 1);
    }
    return sum + lineCost;
  }, 0);
}

function formatCost(v: number) {
  if (!v) return '-';
  return '¥ ' + v.toFixed(2);
}

// 初始加载
search(1);
</script>

<style scoped>
.package-method-page { display:flex; flex-direction:column; gap:1.2rem; animation: pageSlideIn .6s cubic-bezier(.16,1,.3,1); }
@keyframes pageSlideIn { from {opacity:0; transform: translateY(20px);} to {opacity:1; transform:translateY(0);} }
.page-header { display:flex; flex-wrap:wrap; justify-content:space-between; align-items:flex-end; gap:1rem; padding:1rem 1.25rem 1.05rem; background:linear-gradient(145deg,#ffffff,#f8fafc); border:1px solid #e2e8f0; border-radius:16px; box-shadow:0 4px 20px -4px rgba(15,23,42,.15),0 2px 8px -2px rgba(15,23,42,.1); position:relative; overflow:hidden; }
.page-header:before { content:''; position:absolute; top:0; left:0; right:0; height:3px; background:linear-gradient(90deg,#3b82f6,#8b5cf6,#06b6d4); opacity:.8; }
.title-wrap { display:flex; flex-direction:column; gap:.4rem; }
.title-wrap h2 { margin:0; font-size:24px; font-weight:700; letter-spacing:-.025em; color:#0f172a; background:linear-gradient(135deg,#1e293b,#475569); background-clip:text; -webkit-background-clip:text; -webkit-text-fill-color:transparent; }
.title-wrap .sub { font-size:13px; color:#64748b; font-weight:500; letter-spacing:.01em; }
.actions { display:flex; gap:.75rem; align-items:center; flex-wrap:wrap; }
.search-box { display:flex; align-items:center; gap:.5rem; padding:.6rem .8rem; border:1px solid #cbd5e1; border-radius:12px; background:#fff; min-width:260px; transition:all .3s cubic-bezier(.4,0,.2,1); position:relative; }
.search-box:focus-within { border-color:#3b82f6; box-shadow:0 0 0 4px rgba(59,130,246,.15),0 4px 12px -2px rgba(59,130,246,.2); transform:translateY(-1px); }
.search-icon { width:18px; height:18px; color:#6b7280; transition:color .3s; }
.search-box:focus-within .search-icon { color:#3b82f6; }
.search-box input { border:none; outline:none; font-size:14px; flex:1; font-weight:500; color:#374151; }
.search-box input::placeholder { color:#94a3b8; }
.status-select { padding:.6rem .8rem; border:1px solid #cbd5e1; border-radius:12px; font-size:14px; background:#fff; color:#374151; font-weight:500; transition:all .3s cubic-bezier(.4,0,.2,1); cursor:pointer; }
.status-select:focus { outline:none; border-color:#3b82f6; box-shadow:0 0 0 4px rgba(59,130,246,.15); }
.status-select:hover { border-color:#a0aec0; }
.btn { position:relative; cursor:pointer; border:1px solid transparent; padding:.65rem 1.15rem; border-radius:12px; font-size:14px; font-weight:600; letter-spacing:.02em; line-height:1; display:inline-flex; align-items:center; gap:.55rem; transition:all .3s cubic-bezier(.4,0,.2,1); }
.btn.primary { background:linear-gradient(135deg,#3b82f6,#2563eb); color:#fff; border-color:#2563eb; }
.btn.primary:hover { background:linear-gradient(135deg,#2563eb,#1d4ed8); transform:translateY(-2px); }
.btn.ghost { background:rgba(255,255,255,.85); border:1px solid #e2e8f0; color:#374151; }
.btn.ghost.blue { color:#2563eb; }
.btn.ghost.blue:hover { background:#eff6ff; border-color:#bfdbfe; }
.btn.ghost.danger { color:#dc2626; }
.btn.ghost.danger:hover { background:#fef2f2; border-color:#fecaca; }
.btn.sm { padding:.5rem .8rem; font-size:13px; border-radius:10px; }
.btn.view { background:linear-gradient(135deg,#ffffff,#f8fafc); border:1px solid #e2e8f0; color:#374151; padding:.5rem .85rem; border-radius:20px; }
.btn.view:hover { border-color:#3b82f6; color:#1e40af; }
.btn.elevate { box-shadow:0 4px 14px -2px rgba(59,130,246,.4),0 2px 6px -1px rgba(59,130,246,.2); }
.btn.elevate:hover { box-shadow:0 8px 25px -3px rgba(59,130,246,.4),0 4px 12px -2px rgba(59,130,246,.3); }
.btn.create { display:flex; align-items:center; padding:.6rem 1.05rem; font-size:13.5px; font-weight:600; letter-spacing:.01em; }
.btn.create .icon-wrap { width:20px; height:20px; display:inline-flex; align-items:center; justify-content:center; background:rgba(255,255,255,.18); border:1px solid rgba(255,255,255,.35); border-radius:8px; backdrop-filter:blur(2px); }
.btn.create .btn-icon { width:14px; height:14px; stroke:currentColor; }
.btn.create .txt { position:relative; top:.5px; }
.btn.create:hover .icon-wrap { background:rgba(255,255,255,.3); }
.cost-val { font-weight:600; color:#1e40af; text-align:right; }
.status-badge { display:inline-flex; align-items:center; padding:.3rem .65rem; border-radius:8px; font-size:12px; font-weight:600; }
.status-badge.enabled { background:linear-gradient(135deg,#dcfce7,#bbf7d0); color:#166534; border:1px solid #86efac; }
.status-badge.disabled { background:linear-gradient(135deg,#fee2e2,#fecaca); color:#7f1d1d; border:1px solid #fca5a5; }
.entry-table { width:100%; border-collapse:separate; border-spacing:0; font-size:14px; }
.entry-table.fancy thead th { background:linear-gradient(135deg,#f8fafc,#f1f5f9); font-weight:700; font-size:12px; letter-spacing:.05em; text-transform:uppercase; color:#475569; position:sticky; top:0; z-index:1; border-bottom:2px solid #e2e8f0; }
.entry-table th,.entry-table td { padding:.8rem .9rem; text-align:left; }
.entry-table tbody tr { transition:all .3s cubic-bezier(.4,0,.2,1); }
.entry-table tbody tr:hover { background:linear-gradient(135deg,#f8fafc,#f1f5f9); box-shadow:0 4px 12px -2px rgba(59,130,246,.15); border-left:3px solid var(--primary-500); }
.strong { font-weight:600; color:#0f172a; }
.ops { display:flex; gap:.45rem; width:130px; }
.pagination { display:flex; align-items:center; gap:.6rem; justify-content:flex-end; padding:.8rem 0; margin-top:.8rem; border-top:1px solid #e2e8f0; }
.pager-info { font-size:12px; color:#475569; font-weight:500; }
.empty-tip { color:#64748b; text-align:center; padding:3rem 0; font-size:16px; font-weight:500; }
.loading-mask { display:flex; align-items:center; justify-content:center; padding:3rem; color:#64748b; font-weight:600; }
</style>
